/*
 * Copyright (C), 2020, QINGCLOUD
 *
 * Author: 邓哲航
 * Date: 2020/6/2 14:58
 * History:
 * <author>    <time>    <version>    <desc>
 *
 */
package com.qingcloud.keycloakssoplugin;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qingcloud.bo.authc.SsoUserBo;
import com.qingcloud.ssoextension.SsoClientExtension;
import com.qingcloud.ssoextension.exception.SsoUnauthenticationException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * keycloak SSO客户端实现
 *
 * @author 田俊军
 * @since iFCloud3.0
 */
@Component
public class KeycloakSsoClientExtensionImpl implements SsoClientExtension {
    private static final Logger log = LoggerFactory.getLogger(KeycloakSsoClientExtensionImpl.class);

    //客户端id
    private static final String clientId = "ifcloud";

    //keycloak 的域
    private static final String REALM = "master";

    //客户端回调地址
    private static final String redirect_uri = "http://139.198.188.200:10600/ifcloud-security/authc/ssoLogin";

    //获取token url
    private static final String TOKEN_ENDPOINT = "%s/auth/realms/%s/protocol/openid-connect/token";

    //获取用户信息 url
    private static final String CHECK_ENDPOINT = "%s/auth/realms/%s/protocol/openid-connect/userinfo";

    //身份认证key
    private static final String IDENTITY_KEY = "Authorization";

    //token前缀
    private static final String TOKEN_PREFIX = "Bearer ";

    private RestTemplate restTemplate;

    @Override
    public SsoUserBo authenticate(String ssoServerAddress, HttpServletRequest request) throws SsoUnauthenticationException {
        String code = request.getParameter("code");
        //code不为空时，需要根据code获取token
        if(StringUtils.isNotBlank(code)){
            Map<String, Object> param = new HashMap<>();
            param.put("client_id", clientId);
            param.put("grant_type", "authorization_code");
            param.put("code", code);
            param.put("redirect_uri", redirect_uri);
            MultiValueMap<String, Object> postParameters = new LinkedMultiValueMap<>();
            postParameters.setAll(param);

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(postParameters, headers);
            String url = String.format(TOKEN_ENDPOINT, ssoServerAddress, REALM);
            String tokenResult = restTemplate.postForObject(url, httpEntity, String.class);
            if(StringUtils.isNotBlank(tokenResult) && tokenResult.contains("access_token")){
                JSONObject jsonObject = JSON.parseObject(tokenResult);
                String token = jsonObject.getString("access_token");

                log.info("获取的 sso token: {}", token);

                // 请求SSO server进行获取用户信息
                SsoUserBo ssoUserBo = getSsoUserBo(ssoServerAddress, token);
                return ssoUserBo;
            }
        }

        String token = this.getIdentity(request);
        log.info("从请求header获取的token: {}", token);
        if (StringUtils.isBlank(token)) {
            throw new SsoUnauthenticationException();
        }
        //若以前缀开头，去掉token前缀
        if(token.startsWith(TOKEN_PREFIX)){
            token = token.replace(TOKEN_PREFIX, "");
        }

        // 请求SSO server进行校验
        SsoUserBo ssoUserBo = getSsoUserBo(ssoServerAddress, token);
        return ssoUserBo;
    }

    private SsoUserBo getSsoUserBo(String ssoServerAddress, String token) {
        String url = String.format(CHECK_ENDPOINT, ssoServerAddress, REALM);
        HttpHeaders headers = new HttpHeaders();
        headers.add(IDENTITY_KEY, TOKEN_PREFIX + token);
        HttpEntity<String> entity = new HttpEntity<>(headers);
        ResponseEntity<String> result = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);

        SsoUserBo ssoUserBo = new SsoUserBo();
        if(result.getStatusCode() == HttpStatus.OK){
            JSONObject jsonObject = JSON.parseObject(result.getBody());
            ssoUserBo.setRemoteId(jsonObject.getString("sub"));
            ssoUserBo.setUsername(jsonObject.getString("preferred_username"));
            ssoUserBo.setRealName(jsonObject.getString("preferred_username"));
            ssoUserBo.setIdentity(token);
        }
        return ssoUserBo;
    }

    @Override
    public String getIdentity(HttpServletRequest request) {
        return request.getHeader(IDENTITY_KEY);
    }

    @Autowired
    @Qualifier("restTemplate")
    public void setRestTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public String getTenantId(HttpServletRequest request) {
        return null;
    }

}
